/**
*** 
***  Copyright (c) 2004-2007, Masaki OSHITA (www.oshita-lab.org)
**/


#include "stdafx.h"
#include <fstream>
#include <string.h>

#include "BVH.h"





BVH::BVH()
{
	motion = NULL;
	pathCalculated = false;
	Clear();
}


BVH::BVH( const char * bvh_file_name )
{
	motion = NULL;
	pathCalculated = false;
	Clear();

	Load( bvh_file_name );
}

BVH::~BVH()
{
	Clear();
}



void  BVH::Clear()
{
	int  i;
	for ( i=0; i<channels.size(); i++ )
		delete  channels[ i ];
	for ( i=0; i<joints.size(); i++ )
		delete  joints[ i ];
	if ( motion != NULL )
		delete  motion;

	is_load_success = false;
	is_save_success = false;
	
	file_name = "";
	motion_name = "";
	
	num_channel = 0;
	channels.clear();
	joints.clear();
	joint_index.clear();
	
	num_frame = 0;
	interval = 0.0;
	motion = NULL;
}



//

//
void  BVH::Load( const char * bvh_file_name )
{
	#define  BUFFER_LENGTH  1024*4

	ifstream  file;
	char      line[ BUFFER_LENGTH ];
	char *    token;
	char      separater[] = " :,\t";
	vector< Joint * >   joint_stack;
	Joint *   joint = NULL;
	Joint *   new_joint = NULL;
	bool      is_site = false;
	double    x, y ,z;
	int       i, j;


	Clear();

	file_name = bvh_file_name;
	const char *  mn_first = bvh_file_name;
	const char *  mn_last = bvh_file_name + strlen( bvh_file_name );
	if ( strrchr( bvh_file_name, '\\' ) != NULL )
		mn_first = strrchr( bvh_file_name, '\\' ) + 1;
	else if ( strrchr( bvh_file_name, '/' ) != NULL )
		mn_first = strrchr( bvh_file_name, '/' ) + 1;
	if ( strrchr( bvh_file_name, '.' ) != NULL )
		mn_last = strrchr( bvh_file_name, '.' );
	if ( mn_last < mn_first )
		mn_last = bvh_file_name + strlen( bvh_file_name );
	motion_name.assign( mn_first, mn_last );


	file.open( bvh_file_name, ios::in );
	if ( file.is_open() == 0 )  return; 

	
	while ( ! file.eof() )
	{
		
		if ( file.eof() )
		{
			file.close();
			return;
		}

	
		file.getline( line, BUFFER_LENGTH );
		token = strtok( line, separater );

		if ( token == NULL )  continue;

	
		if ( strcmp( token, "{" ) == 0 )
		{
		
			joint_stack.push_back( joint );
			joint = new_joint;
			continue;
		}
		
		if ( strcmp( token, "}" ) == 0 )
		{
		
			joint = joint_stack.back();
			joint_stack.pop_back();
			is_site = false;
			continue;
		}

	
		if ( ( strcmp( token, "ROOT" ) == 0 ) ||
		     ( strcmp( token, "JOINT" ) == 0 ) )
		{
		
			new_joint = new Joint();
			new_joint->index = joints.size();
			new_joint->parent = joint;
			new_joint->has_site = false;
			new_joint->offset[0] = 0.0;  new_joint->offset[1] = 0.0;  new_joint->offset[2] = 0.0;
			new_joint->site[0] = 0.0;  new_joint->site[1] = 0.0;  new_joint->site[2] = 0.0;
			joints.push_back( new_joint );
			if ( joint )
				joint->children.push_back( new_joint );


			token = strtok( NULL, "" );
			while ( *token == ' ' )  token ++;
			new_joint->name = token;

	
			joint_index[ new_joint->name ] = new_joint;
			continue;
		}

	
		if ( ( strcmp( token, "End" ) == 0 ) )
		{
			new_joint = joint;
			is_site = true;
			continue;
		}

	
		if ( strcmp( token, "OFFSET" ) == 0 )
		{
	
			token = strtok( NULL, separater );
			x = token ? atof( token ) : 0.0;
			token = strtok( NULL, separater );
			y = token ? atof( token ) : 0.0;
			token = strtok( NULL, separater );
			z = token ? atof( token ) : 0.0;
			
		
			if ( is_site )
			{
				joint->has_site = true;
				joint->site[0] = x;
				joint->site[1] = y;
				joint->site[2] = z;
			}
			else
			{
				joint->offset[0] = x;
				joint->offset[1] = y;
				joint->offset[2] = z;
			}
			continue;
		}


		if ( strcmp( token, "CHANNELS" ) == 0 )
		{
		
			token = strtok( NULL, separater );
			joint->channels.resize( token ? atoi( token ) : 0 );


			for ( i=0; i<joint->channels.size(); i++ )
			{
		
				Channel *  channel = new Channel();
				channel->joint = joint;
				channel->index = channels.size();
				channels.push_back( channel );
				joint->channels[ i ] = channel;

		
				token = strtok( NULL, separater );
				if ( strcmp( token, "Xrotation" ) == 0 )
					channel->type = X_ROTATION;
				else if ( strcmp( token, "Yrotation" ) == 0 )
					channel->type = Y_ROTATION;
				else if ( strcmp( token, "Zrotation" ) == 0 )
					channel->type = Z_ROTATION;
				else if ( strcmp( token, "Xposition" ) == 0 )
					channel->type = X_POSITION;
				else if ( strcmp( token, "Yposition" ) == 0 )
					channel->type = Y_POSITION;
				else if ( strcmp( token, "Zposition" ) == 0 )
					channel->type = Z_POSITION;
			}
		}


		if ( strcmp( token, "MOTION" ) == 0 )
			break;
	}



	file.getline( line, BUFFER_LENGTH );
	token = strtok( line, separater );
	if ( strcmp( token, "Frames" ) != 0 )
	{
		file.close();
		return;
	}
	token = strtok( NULL, separater );
	if ( token == NULL )
	{
		file.close();
		return;
	}
	num_frame = atoi( token );

	file.getline( line, BUFFER_LENGTH );
	token = strtok( line, ":" );
	if ( strcmp( token, "Frame Time" ) != 0 )
	{
		file.close();
		return;
	}
	token = strtok( NULL, separater );
	if ( token == NULL )
	{
		file.close();
		return;
	}
	interval = atof( token );

	num_channel = channels.size();
	motion = new double[ num_frame * num_channel ];


	for ( i=0; i<num_frame; i++ )
	{
		file.getline( line, BUFFER_LENGTH );
		token = strtok( line, separater );
		for ( j=0; j<num_channel; j++ )
		{
			if ( token == NULL )
			{
				file.close();
				return;
			}
			motion[ i*num_channel + j ] = atof( token );
			token = strtok( NULL, separater );
		}
	}


	file.close();


	is_load_success = true;
}

void BVH::Save( const char * bvh_file_name )
{
	ofstream  file;
	save_file_name = bvh_file_name;

	file.open( bvh_file_name );
	if ( file.is_open() == 0 )  return;

	HIERARCHY;
	NEXT_LINE;
	AddJoint(file, joints[0]);

	// Motion...
	MOTION;
	NEXT_LINE;
	FRAMES;
	NEXT_LINE;
	FRAME_TIME;
	NEXT_LINE;
	for (int i = 0; i < this->GetNumFrame(); i++)
	{
		for (int j = 0; j < this->GetNumChannel(); j++)
			MOTION_DATA;
		NEXT_LINE;
	}

	file.close();
	is_save_success = true;
}

void  BVH::AddJoint( ofstream &file, const Joint *joint )
{
	static int layer = 0;
	INDENT;
	// ROOT or JOINT
	if (layer == 0) ROOT;
	else JOINT;
	JOINT_NAME;
	NEXT_LINE;
	INDENT;
	BEGIN_BRACKET;
	NEXT_LINE;

	layer++;

	// OFFSET
	INDENT;
	OFFSET;
	JOINT_OFFSET;
	NEXT_LINE;

	// CHANNELS
	INDENT;
	CHANNELS;
	for (int j = 0; j < joint->channels.size(); j++)
		switch (joint->channels[j]->type)
		{
		case X_POSITION:  file << "\tXposition"; break;
		case Y_POSITION:  file << "\tYposition"; break;
		case Z_POSITION:  file << "\tZposition"; break;
		case X_ROTATION: file << "\tXrotation "; break;
		case Y_ROTATION: file << "\tYrotation "; break;
		case Z_ROTATION: file << "\tZrotation ";
		}
	NEXT_LINE;

	// End Site or Children Joints
	if ( joint->has_site )		// End Site
	{
		INDENT;
		END_SITE;
		NEXT_LINE;
		INDENT;
		BEGIN_BRACKET;
		NEXT_LINE;

		layer++;

		INDENT;
		OFFSET;
		SITES;
		NEXT_LINE;

		layer--;

		INDENT;
		CLOSE_BRACKET;	// End Site
		NEXT_LINE;

		layer--;

		INDENT;
		CLOSE_BRACKET;	// Joint
		NEXT_LINE;
	}
	else		// Children joints
	{
		// Add children joints
		for ( int i = 0; i < joint->children.size(); i++)
			AddJoint(file, joint->children[i]);

		layer--;
		
		INDENT;
		CLOSE_BRACKET;
		NEXT_LINE;
	}
}



// End of BVH.cpp
